' Strict
SuperStrict

' Modules
Import BRL.Max2D
'Import BtbN.GLDraw2D
Import BRL.PNGLoader

' Files
Import "../TGame.bmx"
Import "../TFrameRate.bmx"
Import "../TPlayer.bmx"
Import "../TTileMap.bmx"
Import "../TINILoader.bmx"
Import "../GUI/TGUI.bmx"
Import "../Utilities/Graphics.bmx"

' Global
Global gsEditor:TGameStateEditor

' TGameStateEditor
Type TGameStateEditor Extends TGameState
	Const SCROLL_BORDER:Int = 10
	
	Field frameCounter:TFrameRate
	Field map:TTileMap
	Field font:TImageFont
	Field regionPreviewFont:TImageFont
	Field currentTile:Byte
	Field currentLayer:Byte
	Field currentEnemyType:String
	Field isEditing:Int
	Field scrollSpeed:Float
	Field loggerEditor:TLog
	
	' Options
	Field viewGrid:Int
	Field viewCollision:Int
	Field viewStart:Int
	Field viewTemplate:Int
	Field filterTemplate:Int
	
	' GUI
	Field gui:TGUI
	Field guiFont:TImageFont
	Field guiFontTitle:TImageFont
	Field infoWindow:TWindow
	Field posLabel:TLabel
	Field tilesDrawnLabel:TLabel
	Field tileWindow:TWindow
	Field optionsWindow:TWindow
	Field generateButton:TButton
	Field clearButton:TButton
	Field mapOptions:TGroup
	Field tileMenu:TPopupMenu
	
	' Images
	Field arrowImg:TImage
	Field templateImg:TImage
	Field templatePixmap:TPixmap
	
	' Cursor
	Field x:Int
	Field y:Int
	
	' Tile menu
	Field savedX:Int
	Field savedY:Int
	
	' Init
	Method Init()
		Self.isEditing = 0
		
		Self.currentEnemyType = "ice-queen"
		
		Self.currentLayer = 0
		Self.x = 0
		Self.y = 0
		
		' Init
		SetBlend ALPHABLEND
		SetMaskColor 255, 255, 255
		MoveMouse game.gfxWidthHalf, game.gfxHeightHalf
		
		' Frame counter
		frameCounter = TFrameRate.Create()
		
		' Init resources
		Self.InitResources()
		
		' Init GUI
		Self.InitGUI()
		
		' TileMap
		Self.InitEngine(FS_ROOT + "data/engine.ini")
		
		' TODO: Remove hardcoded stuff
		Self.SetScrollSpeed(2)
		Self.currentTile = 10
		
		' Detail Log
		?Debug
			Self.loggerEditor = TLog.Create(StandardIOStream)
		?Not Debug
			Self.loggerEditor = TLog.Create(WriteFile(FS_ROOT + "logs/editor.txt"))
		?
		
		' Memory usage
		Self.loggerEditor.Write("GC Memory allocated: " + GCMemAlloced() + " bytes")
	End Method
	
	' InitResources
	Method InitResources()
		' Load fonts
		Local mainFont:String = "tahoma.ttf"
		Self.font = LoadImageFont(mainFont, 22, SMOOTHFONT)
		Self.guiFont = LoadImageFont(mainFont, 12, SMOOTHFONT)
		Self.guiFontTitle = LoadImageFont(mainFont, 16, SMOOTHFONT)
 		Self.regionPreviewFont = LoadImageFont("Campaign-Normal.ttf", 72, SMOOTHFONT) 
		
		' Load images
		Self.arrowImg = LoadImage(FS_ROOT + "data/editor/arrow.png")
		MidHandleImage Self.arrowImg
		
		' Map template
		Self.LoadTemplate()
	End Method
	
	' InitEngine
	Method InitEngine(iniFile:String)
		' TileMap
		Self.map = TTileMap.Create() 
		Self.map.LoadINI(FS_ROOT + "data/engine.ini")
		Self.map.SetScreenSize(game.gfxWidth, game.gfxHeight)
		Self.map.InitTextureAtlas() 
		Self.map.LoadLayers(FS_ROOT + "data/layer-", Self.tileWindow, TGameStateEditor.SetTileByWidget)
		'Self.map.Load(FS_ROOT + "data/maps/lyphia.map")
		Self.map.Resize(2000, 2000, Self.map.layers)
		'Self.map.Resize(2000, 2000, 2)
		
		' Change tile window layout
		Self.tileWindow.SetPositionAbs(5, game.gfxHeight - Self.map.tileSizeY * 10 - 5)
		Self.tileWindow.SetSizeAbs(Self.map.tileSizeX * 5, Self.map.tileSizeY * 10)
		Self.tileWindow.ApplyLayoutTable(10, 5)
		
		' TODO: Remove hardcoded stuff
		'Self.map.FillAll(0, 1)
		'Self.map.FillAll(1, 0)
	End Method
	
	' InitGUI
	Method InitGUI()
		Self.gui = TGUI.Create()
		
		' Info window
		Self.infoWindow = TWindow.Create("infoWindow", "Info", game.gfxWidth - 205, game.gfxHeight - 105, 200, 100)
		Self.gui.Add(infoWindow)
		
		Local gfxLabel:TLabel = TLabel.Create("gfxLabel", game.gfxWidth + " x " + game.gfxHeight, 5, 10)
		Self.infoWindow.Add(gfxLabel) 
		
		Self.posLabel = TLabel.Create("posLabel", "", 5, 10 + gfxLabel.textSizeY + 5)
		Self.infoWindow.Add(Self.posLabel)
		
		Self.tilesDrawnLabel = TLabel.Create("tilesDrawLabel", "", 5, 10 + gfxLabel.textSizeY * 2 + 5 * 2)
		Self.infoWindow.Add(Self.tilesDrawnLabel)
		
		' Tile window
		Self.tileWindow = TWindow.Create("tileWindow", "Tiles")
		Self.gui.Add(Self.tileWindow) 
		
		' Options window
		Self.optionsWindow = TWindow.Create("optionsWindow", "Options", game.gfxWidth - 205, 5 + 24, 200, 400) 
		Self.optionsWindow.SetPadding(5, 5, 5, 5)
		Self.gui.Add(Self.optionsWindow)
		
		' Map options
		Self.mapOptions = TGroup.Create("mapOptions", "Map options", 0, 0)
		Self.mapOptions.SetSize(1.0, 0.5)
		Self.mapOptions.SetColor(192, 192, 192)
		Self.optionsWindow.Add(Self.mapOptions)
		
		Local gridCheckBox:TCheckBox = TCheckBox.Create("gridCheckBox", "Grid", 5, 5)
		gridCheckBox.onClick = TGameStateEditor.ToggleGrid
		Self.mapOptions.Add(gridCheckBox)
		
		Local movementCheckBox:TCheckBox = TCheckBox.Create("movementCheckBox", "Collision", 5, 30)
		movementCheckBox.onClick = TGameStateEditor.ToggleMovement
		Self.mapOptions.Add(movementCheckBox)
		
		Local startTileCheckBox:TCheckBox = TCheckBox.Create("startCheckBox", "Start", 5, 55) 
		startTileCheckBox.onClick = TGameStateEditor.ToggleStart
		startTileCheckBox.SetState(True)
		Self.mapOptions.Add(startTileCheckBox)
		
		Local templCheckBox:TCheckBox = TCheckBox.Create("templCheckBox", "Template", 5, 80)
		templCheckBox.onClick = TGameStateEditor.ToggleTemplate
		Self.mapOptions.Add(templCheckBox)
		
		Local templFilteredCheckBox:TCheckBox = TCheckBox.Create("templFilteredCheckBox", "Filter Template", 5, 105)
		templFilteredCheckBox.onClick = TGameStateEditor.ToggleTemplateFilter
		Self.mapOptions.Add(templFilteredCheckBox) 
		
		Local zoomSlider:TSlider = TSlider.Create("zoomSlider", "Zoom", 0, 130)
		zoomSlider.SetMinMax(0.125, 2.0)
		zoomSlider.SetStandardValue(1.0)
		zoomSlider.SetSize(1.0, 0) 
		zoomSlider.SetSizeAbs(0, 24) 
		zoomSlider.onSlide = TGameStateEditor.ChangeZoom
		Self.mapOptions.Add(zoomSlider)
		
		Self.generateButton:TButton = TButton.Create("generateButton", "Generate from Template", 0, 0) 
		Self.generateButton.SetSize(1.0, 0) 
		Self.generateButton.SetSizeAbs(0, 24) 
		Self.generateButton.SetPosition(0, 1.0)
		Self.generateButton.SetPositionAbs(0, -24)
		Self.generateButton.onClick = TGameStateEditor.GenerateFromTemplate
		Self.optionsWindow.Add(Self.generateButton)
		
		Self.clearButton:TButton = TButton.Create("clearButton", "Clear layer", 0, 0)
		Self.clearButton.SetSize(1.0, 0)
		Self.clearButton.SetSizeAbs(0, 24)
		Self.clearButton.SetPosition(0, 1.0)
		Self.clearButton.SetPositionAbs(0, -24 * 2 - 1)
		Self.clearButton.onClick = TGameStateEditor.ClearLayer
		Self.optionsWindow.Add(Self.clearButton)
		
		' Menu
		Self.InitGUIMenu()
		
		' Cursors
		Self.gui.SetCursor("default")
		
		' Apply font to all widgets
		Self.gui.SetFont(Self.guiFont)
		
		' Fonts
		Self.infoWindow.SetFont(Self.guiFontTitle)
		Self.tileWindow.SetFont(Self.guiFontTitle)
		Self.optionsWindow.SetFont(Self.guiFontTitle)
	End Method
	
	' InitGUIMenu
	Method InitGUIMenu() 
		Local menuBar:TMenuBar = TMenuBar.Create("menuBar", "Editor menu", 0, 0) 
		menuBar.SetSizeAbs(0, 24) 
		menuBar.SetSize(1.0, 0) 
		menuBar.SetAlpha(0.75)
		Self.gui.root.Add(menuBar) 
		
		' File
		Local popupFile:TPopupMenu = TPopupMenu.Create("menuFile") 
		menuBar.AddMenu("File", popupFile) 
		popupFile.AddMenuEntry("Load", TGameStateEditor.LoadFunc) 
		popupFile.AddMenuEntry("Save", TGameStateEditor.SaveFunc)
		
		' Popup for root
		Self.tileMenu = TPopupMenu.Create("menuTile")
		Self.gui.AddMenu(Self.tileMenu)
		Self.tileMenu.AddMenuEntry("Copy", TGameStateEditor.CopyFunc)
		Self.tileMenu.AddMenuEntry("Fill", TGameStateEditor.FillFunc)
		Self.tileMenu.AddMenuEntry("Add Human-1 spawn point", TGameStateEditor.AddEnemySpawnFunc)
		Self.tileMenu.AddMenuEntry("Set start point", TGameStateEditor.SetStartTileFunc)
	End Method
	
	' LoadTemplate
	Method LoadTemplate()
		Self.templatePixmap = LoadPixmap(FS_ROOT + "data/maps/lyphia-template.png")
		Self.templateImg = LoadImage(Self.templatePixmap, Self.filterTemplate * FILTEREDIMAGE) 
	End Method
	
	' Update
	Method Update()
		' Update input system
		TInputSystem.Update()
		
		' Update GUI system
		If Self.isEditing = 0
			Self.gui.Update()
		EndIf
		
		' Update
		frameCounter.Update()
		
		' Clear screen
		Cls
		
		' Reset color, alpha, rotation
		ResetMax2D()
		
		' Scrolling
		Self.ScrollKeyboard()
		'Self.ScrollMouse()
		
		' Draw map
		Self.map.Draw()
		
		' Map offset
		SetOrigin Int(Self.map.originX), Int(Self.map.originY)
		
		Self.map.DrawEnemySpawns()
		
		' Grid
		If Self.viewGrid
			Self.map.DrawGrid()
		EndIf
		
		' Movement view
		If Self.viewCollision
			Self.map.DrawMovement(Self.currentLayer, Self.arrowImg)
		EndIf
		
		' Start view
		If Self.viewStart
			Self.map.DrawStartTile()
		EndIf
		
		' Template
		If Self.viewTemplate
			SetScale Self.map.width * Self.map.tileSizeX / Float(Self.templateImg.width), Self.map.height * Self.map.tileSizeY / Float(Self.templateImg.height)
			SetAlpha 0.5
			SetColor 255, 255, 255
			DrawImage Self.templateImg, 0, 0
			SetScale 1, 1
		EndIf
		
		' Reset offset
		SetOrigin 0, 0
		
		' Test
		Rem
		SetBlend LIGHTBLEND
		SetColor 32, 32, 32
		DrawRect 0, 0, GraphicsWidth(), GraphicsHeight() 
		SetBlend ALPHABLEND
		End Rem
		
		' Edit map
		Self.Edit()
		
		' Scroll border
		'SetAlpha 0.15
		'SetColor 255, 255, 255
		'DrawRectOutline 0, 0, game.gfxWidth, game.gfxHeight, SCROLL_BORDER
		
		' GUI
		Self.infoWindow.SetText("FPS: " + Self.frameCounter.GetFPS())	' TODO: Custom frame counter function
		Self.tilesDrawnLabel.SetText("Tiles drawn: " + Self.map.GetNumberOfTilesOnScreen())
		
		' More alpha while editing
		'If Self.isEditing = 1
		'	Self.gui.SetAlpha 0.5
		'Else
		'	Self.gui.SetAlpha 0.75
		'EndIf
		
		Self.gui.SetAlpha 1
		Self.gui.Draw()
		
		' Swap buffers
		Flip game.vsync
		
		' Ctrl
		If TInputSystem.GetKeyDown(KEY_LCONTROL) 
			' TODO: Remove hardcoded stuff
			If TInputSystem.GetKeyHit(KEY_S)
				Self.map.Save(FS_ROOT + "data/maps/lyphia.map")
			EndIf
			
			If TInputSystem.GetKeyHit(KEY_L)
				Self.map.Load(FS_ROOT + "data/maps/lyphia.map")
			EndIf
			
			' Ctrl + E = Open Editor
			If TInputSystem.GetKeyHit(KEY_E)
				game.SetGameStateByName("InGame")
			EndIf
		EndIf
		
		' Quit
		If TInputSystem.GetKeyHit(KEY_ESCAPE)
			' TODO: Set "main menu" state
			game.SetGameState(Null)
		EndIf
	End Method
	
	' Edit
	Method Edit()
		' Layer
		Self.currentLayer = (Self.currentLayer + TInputSystem.GetMouseZSpeed()) Mod map.layers
		
		' Get coordinates
		If Self.map.GetTileCoords(TInputSystem.GetMouseX(), TInputSystem.GetMouseY(), Self.x, Self.y)
			Self.posLabel.SetText("Layer " + Self.currentLayer + " | " + "X: " + Self.x + ", Y: " + Self.y)
			
			'SetAlpha 0.25
			'SetColor 0, 0, 0
			'DrawRectOutline 1 + Int(Self.map.originX) + Self.x * Self.map.tileSizeX, 1 + Int(Self.map.originY) + Self.y * Self.map.tileSizeY, Self.map.tileSizeX, Self.map.tileSizeY
			
			SetAlpha 0.5
			SetColor 255, 255, 255
			DrawRect Int(Self.map.originX) + Self.x * Self.map.tileSizeX, Int(Self.map.originY) + Self.y * Self.map.tileSizeY, Self.map.tileSizeX, Self.map.tileSizeY
			DrawRectOutline Int(Self.map.originX) + Self.x * Self.map.tileSizeX, Int(Self.map.originY) + Self.y * Self.map.tileSizeY, Self.map.tileSizeX, Self.map.tileSizeY
			
			Self.isEditing = 0
			
			' Edit
			If TInputSystem.GetMouseDown(1)
				Self.map.Set(Self.currentLayer, Self.x, Self.y, Self.currentTile)
				Self.isEditing = True
			EndIf
			
			If TInputSystem.GetMouseHit(2)
				Self.savedX = Self.x
				Self.savedY = Self.y
				Self.tileMenu.Popup()
				Self.isEditing = True
			EndIf
			
			'If TInputSystem.GetMouseDown(3)
			'	Self.map.FillAt(Self.currentLayer, Self.x, Self.y, Self.currentTile)
			'	Self.isEditing = True
			'EndIf
		Else
			Self.posLabel.SetText("")
		EndIf
	End Method
	
	' SetScrollSpeed
	Method SetScrollSpeed(nSpeed:Float)
		Self.scrollSpeed = nSpeed
	End Method
	
	' ScrollKeyboard
	Method ScrollKeyboard()
		' Scroll left
		If TInputSystem.GetKeyDown(KEY_LEFT)
			Self.map.Scroll(-game.speed * Self.scrollSpeed * 2, 0)
		EndIf
		
		' Scroll right
		If TInputSystem.GetKeyDown(KEY_RIGHT)
			Self.map.Scroll(game.speed * Self.scrollSpeed * 2, 0)
		EndIf
		
		' Scroll up
		If TInputSystem.GetKeyDown(KEY_UP)
			Self.map.Scroll(0, -game.speed * Self.scrollSpeed * 2)
		EndIf
		
		' Scroll down
		If TInputSystem.GetKeyDown(KEY_DOWN)
			Self.map.Scroll(0, game.speed * Self.scrollSpeed * 2)
		EndIf
	End Method
	
	' ScrollMouse
	Method ScrollMouse()
		' Scroll left
		If TInputSystem.GetMouseX() < SCROLL_BORDER
			Self.map.Scroll(-game.speed * Self.scrollSpeed * 0.05 * (SCROLL_BORDER - TInputSystem.GetMouseX()), 0)
		EndIf
		
		' Scroll right
		If TInputSystem.GetMouseX() > game.gfxWidth - SCROLL_BORDER
			Self.map.Scroll(game.speed * Self.scrollSpeed * 0.05 * (SCROLL_BORDER - (game.gfxWidth - TInputSystem.GetMouseX())), 0)
		EndIf
		
		' Scroll up
		If TInputSystem.GetMouseY() < SCROLL_BORDER
			Self.map.Scroll(0, -game.speed * Self.scrollSpeed * 0.05 * (SCROLL_BORDER - TInputSystem.GetMouseY()))
		EndIf
		
		' Scroll down
		If TInputSystem.GetMouseY() > game.gfxHeight - SCROLL_BORDER
			Self.map.Scroll(0, game.speed * Self.scrollSpeed * 0.05 * (SCROLL_BORDER - (game.gfxHeight - TInputSystem.GetMouseY())))
		EndIf
	End Method
	
	' Remove
	Method Remove()
		frameCounter = Null
	End Method
	
	' SetTileByWidget
	Function SetTileByWidget(widget:TWidget)
		gsEditor.currentTile = Int(widget.id)
	End Function
	
	' ToggleGrid
	Function ToggleGrid(widget:TWidget)
		gsEditor.viewGrid = TCheckBox(widget).GetState()
	End Function
	
	' ToggleMovement
	Function ToggleMovement(widget:TWidget)
		gsEditor.viewCollision = TCheckBox(widget).GetState()
	End Function
	
	' ToggleStart
	Function ToggleStart(widget:TWidget)
		gsEditor.viewStart = TCheckBox(widget).GetState()
	End Function
	
	' ToggleTemplate
	Function ToggleTemplate(widget:TWidget)
		gsEditor.viewTemplate = TCheckBox(widget).GetState()
	End Function
	
	' ToggleTemplateFilter
	Function ToggleTemplateFilter(widget:TWidget)
		gsEditor.filterTemplate = TCheckBox(widget).GetState() 
		gsEditor.LoadTemplate()
	End Function
	
	' ChangeZoom
	Function ChangeZoom(widget:TWidget) 
		gsEditor.map.SetZoom(TSlider(widget).GetValue())
	End Function
	
	' ClearLayer
	Function ClearLayer(widget:TWidget) 
		' TODO: Remove hardcoded stuff
		gsEditor.map.FillAll(gsEditor.currentLayer, gsEditor.currentTile)
	End Function
	
	' GenerateFromTemplate
	Function GenerateFromTemplate(widget:TWidget) 
		Local scaleX:Float = gsEditor.map.width / Float(gsEditor.templateImg.width) 
		Local scaleY:Float = gsEditor.map.height / Float(gsEditor.templateImg.height)
		Local oldX:Int
		Local oldY:Int
		Local oldXSet:Int[] = New Int[gsEditor.map.height]
		Local oldYSet:Int
		Local i:Int
		Local h:Int
		Local rgba:Int
		
		' TODO: Remove hardcoded stuff
		' INI
		Local ini:TINI = TINI.Create(FS_ROOT + "data/maps/lyphia.ini") 
		Local autoTerrain:TINICategory
		ini.Load() 
		autoTerrain = ini.GetCategory("AutoTerrain")
		
		' TODO: Remove hardcoded stuff
		Local bc:Int
		
		For i = 0 Until gsEditor.templateImg.width
			For h = 0 Until gsEditor.templateImg.height
				rgba = ReadPixel(gsEditor.templatePixmap, i, h)
				If rgba Shr 24 > 0
					
					bc = Int(autoTerrain.Get("R" + ((rgba & $FF0000) Shr 16)))
					If bc = 0
						bc = gsEditor.currentTile
					EndIf
					
					gsEditor.map.tiles[0, i * scaleX, h * scaleY] = bc
					
					' Fill gaps Y
					If oldYSet 'And Int(h * scaleY) - oldY >= 1
						For Local fillLine:Int = 1 To Int(h * scaleY) - oldY
							gsEditor.map.tiles[0, i * scaleX, oldY + fillLine] = bc
							oldXSet[oldY + fillLine] = Int(h * scaleY) - oldY
						Next
					EndIf
					
					' Fill gaps X
					If oldXSet[h * scaleY] 'And Int(i * scaleX) - oldX >= 1
						For Local fillLineX:Int = 1 To Int(i * scaleX) - oldX
							For Local fillLineY:Int = 1 To Int(h * scaleY) - oldY'oldXSet[h * scaleY]
								gsEditor.map.tiles[0, oldX + fillLineX, oldY + fillLineY] = bc
							Next
						Next
					EndIf
					
					oldXSet[h * scaleY] = True
					oldYSet = True
				Else
					oldXSet[h * scaleY] = False
					oldYSet = False
				EndIf
				
				oldY = Int(h * scaleY)
			Next
			
			oldX = Int(i * scaleX)
		Next
		
		gsEditor.map.RegenerateAutoTileInformation()
	End Function
	
	' LoadFunc
	Function LoadFunc(widget:TWidget)
		gsEditor.map.Load(FS_ROOT + "data/maps/lyphia.map")
		'CreateThread(TGameStateEditor.ThreadLoad, Null)
	End Function
	
	' ThreadLoad
	'Function ThreadLoad:Object(data:Object) 
	'	
	'End Function
	
	' SaveFunc
	Function SaveFunc(widget:TWidget) 
		gsEditor.map.Save(FS_ROOT + "data/maps/lyphia.map")
	End Function
	
	' SetStartTileFunc
	Function SetStartTileFunc(widget:TWidget)
		gsEditor.map.SetStartTile(gsEditor.savedX, gsEditor.savedY)
	End Function
	
	' CopyFunc
	Function CopyFunc(widget:TWidget)
		gsEditor.currentTile = gsEditor.map.Get(gsEditor.currentLayer, gsEditor.savedX, gsEditor.savedY)
	End Function
	
	' FillFunc
	Function FillFunc(widget:TWidget)
		gsEditor.map.FillAt(gsEditor.currentLayer, gsEditor.savedX, gsEditor.savedY, gsEditor.currentTile)
	End Function
	
	' AddEnemySpawnFunc
	Function AddEnemySpawnFunc(widget:TWidget)
		Local spawn:TEnemySpawn = TEnemySpawn.Create(gsEditor.currentEnemyType, gsEditor.savedX, gsEditor.savedY, gsEditor.savedX * gsEditor.map.realTileSizeX, gsEditor.savedY * gsEditor.map.realTileSizeY)
		PrintTypeDebug(spawn, gsEditor.loggerEditor)
		gsEditor.map.enemySpawns[gsEditor.savedY].AddLast(spawn)
	End Function
	
	' ToString
	Method ToString:String()
		Return "Editor"
	End Method
	
	' Create
	Function Create:TGameStateEditor(gameRef:TGame)
		Local gs:TGameStateEditor = New TGameStateEditor
		gameRef.RegisterGameState("Editor", gs)
		Return gs
	End Function
End Type


